#include    "logger.h"
#include    "os.h"

#include    <cstdarg>
#include    <cstdlib>
#include    <cstring>

#if defined(_WIN32)
#	include	<Windows.h>
#endif

static char LOG_LEVEL_DESC[] = "EWIDCCCCCCCCCCC";
static bool SUPPORT_COLOR = false;

Logger::Logger()
	: _file(nullptr)
	, _buf(new char[8192])
	, _name("main")
	, _path("./logs")
	, _capacity(1024 * 1024 * 4)
	, _writed(0)
	, _allowed_max_level(Level::Info)
	, _last_day(-1) {
#if defined(_WIN32)
	HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
	if (hOut == INVALID_HANDLE_VALUE) return;
	DWORD mode = 0;
	if (!GetConsoleMode(hOut, &mode)) return;
	DWORD request = mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN;
	if (!SetConsoleMode(hOut, request)) {
		request = mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING;
		if (SetConsoleMode(hOut, request)) SUPPORT_COLOR = true;
	} else {
		SUPPORT_COLOR = true;
	}
#else
	SUPPORT_COLOR = true;
#endif
}

Logger::~Logger() {
	if (_file) fclose(_file);
	if (_buf) delete[] _buf;
}

Logger & Logger::Instance() {
	static Logger * g_logger = nullptr;
	if (g_logger == nullptr) g_logger = new Logger();
	return *g_logger;
}

void Logger::Initialize(const std::string & name, const std::string & root, Level max_level, size_t capacity) {
	_name = name;
	_path = root;
	_allowed_max_level = max_level;
	_capacity = capacity;
}

void Logger::Write(Level level, const char * file, int line, const char * fmt, ...) {
	if (_allowed_max_level < level) return;

	DateTime now = OS::Now();

	if (_last_day != now.day || _writed >= _capacity) {
		if (_file) fclose(_file);
		_file = nullptr;

		char path[512] = { 0 };
		snprintf(path, 512, "%s/%04d%02d%02d/%s_%02d%02d%02d.%03d.log",
			_path.c_str(), now.year, now.month, now.day, _name.c_str(), now.hour, now.minute, now.second, now.millisecond);
		
		if (OS::CreateDir(OS::GetDirName(path))) {
		#if defined(_WIN32)
			fopen_s(&_file, path, "w+");
		#else
			_file = fopen(path, "w+");
		#endif
			_writed = 0;
			_last_day = now.day;
		}
	}

	if (!_file) return;

	memset(_buf, 0, 8192);

	int offset = snprintf(_buf, 8192, "[%04d-%02d-%02d %02d:%02d:%02d.%03d][%c][%s@%d]",
		now.year, now.month, now.day, now.hour, now.minute, now.second, now.millisecond,
		LOG_LEVEL_DESC[level], file, line);
	if (offset <= 0) return;

	va_list args;
	va_start(args, fmt);
	vsnprintf(_buf + offset, 8192 - offset, fmt, args);
	va_end(args);

	_writed += fprintf(_file, "%s\n", _buf);
	fflush(_file);

	if (!SUPPORT_COLOR || level == Level::Debug) {
		printf("%s\n", _buf);
	} else if (level == Level::Warning) {
		printf("\x1b[33m%s\x1b[0m\n", _buf);
	} else if (level == Level::Error) {
		printf("\x1b[31m%s\x1b[0m\n", _buf);
	} else {
		printf("\x1b[32m%s\x1b[0m\n", _buf);
	}

	fflush(stdout);
}